package org.dubnation.base.util.verifycode.service.impl;

import org.dubnation.base.enums.ResponseCode;
import org.dubnation.base.exception.BusinessException;
import org.dubnation.base.util.verifycode.SendPhoneSmsUtil;
import org.dubnation.base.util.verifycode.service.IVerifyCodeService;
import org.dubnation.base.util.audit.DubnationAssert;
import org.dubnation.base.util.verifycode.dto.SendSmsCodeDto;
import org.dubnation.base.util.verifycode.VerifyCodeUtils;
import org.dubnation.user.domain.LoginInfo;
import org.dubnation.user.mapper.LoginInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.dubnation.base.constants.SystemConstants;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired(required = false)
    private LoginInfoMapper loginInfoMapper;

    @Value("${sms.code.register.resend-time}")
    private Integer resendTime;

    @Value("${sms.code.register.expire-time}")
    private Integer expireTime;

    /**
     * 获取图片验证码
     * @return
     */
    @Override
    public String getImgCode(String uuid) {
        try {
            //1.生成随机验证码
            String code = VerifyCodeUtils.generateVerifyCode(6);
            //2.将随机验证码存储到redis中
            redisTemplate.opsForValue().set(uuid, code, expireTime, TimeUnit.MINUTES);
            //3.将图片验证码进行base64加密之后返回到前端
            return VerifyCodeUtils.verifyCode(150, 40, code);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
        }
    }

    /**
     * 获取手机验证码
     * @param sendSmsCodeDto
     */
    @Override
        public void getPhoneCode(SendSmsCodeDto sendSmsCodeDto) {
        //1.判断该操作是否为注册, 只有注册才用判断图形验证码
        if (sendSmsCodeDto.getCodeType() == null || sendSmsCodeDto.getCodeType().equals("")){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
        }
        if (SystemConstants.REDIS_CODE_REGISTER.equals(sendSmsCodeDto.getCodeType())){
            //2.若为注册操作, 验证图形验证码
            if (sendSmsCodeDto.getImgCode() == null || sendSmsCodeDto.getImgCode().equals("")
                    || sendSmsCodeDto.getUuid() == null || sendSmsCodeDto.getUuid().equals("")){
                throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
            }
            Object imgCodeInRedis = redisTemplate.opsForValue().get(sendSmsCodeDto.getUuid());
            DubnationAssert.isNotNull(imgCodeInRedis, ResponseCode.RESPONSE_CODE_500);
            DubnationAssert.isEqIgnore(imgCodeInRedis.toString(), sendSmsCodeDto.getImgCode(), ResponseCode.RESPONSE_CODE_4000011);
        }
        //3.若图形验证码正确, 判断该手机号是否已注册, 若未注册则获取6位数的随机数验证码, 并发送手机验证码
        LoginInfo loginInfo = loginInfoMapper.queryByPhone(sendSmsCodeDto.getPhone());
        DubnationAssert.isNull(loginInfo, ResponseCode.RESPONSE_CODE_4000014);
        String phoneCode = VerifyCodeUtils.generateVerifyCode(6, "0123456789");
        System.out.println(phoneCode);
        Boolean sendCode = null;
        try {
            //4.做恶意请求拦截, 若在一分钟内请求多次则将其拦断
            Object phoneCodeInRedis = redisTemplate.opsForValue().get(sendSmsCodeDto.getPhone() + ":" + sendSmsCodeDto.getCodeType());
            if (Objects.nonNull(phoneCodeInRedis)){
                //该用户已发送过获取手机验证码请求
                String lastSendTimeStr = phoneCodeInRedis.toString().split(":")[1];
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Long lastSendTime = dateFormat.parse(lastSendTimeStr).getTime(); //上一次请求时间
                Long currentSendTime = System.currentTimeMillis(); //当前请求时间
                if ((currentSendTime - lastSendTime) < 60*1000*resendTime){
                    //在一分钟内请求了两次, 拦截
                    throw new BusinessException(ResponseCode.RESPONSE_CODE_4000013);
                }else {
                    //同一个用户再次获取验证码, 发送和上次相同的验证码
                    phoneCode = phoneCodeInRedis.toString().split(":")[0];
                }
            }
            //发送验证码
            String sendContent = "您的注册验证码为%s, 请在5分钟内进行验证, 谢谢!";
            sendCode = SendPhoneSmsUtil.sendSms(sendSmsCodeDto.getPhone(), String.format(sendContent, phoneCode));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (sendCode == false){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_4000012);
        }
        //5.将手机验证码存储在redis中
        redisTemplate.opsForValue().set(sendSmsCodeDto.getPhone() + ":" + sendSmsCodeDto.getCodeType(),
                phoneCode + ":" + System.currentTimeMillis(), expireTime, TimeUnit.MINUTES);
    }

    /**
     * 检查手机号码是否已注册过
     * @param phone
     */
    @Override
    public void checkPhone(String phone) {
        if (Objects.isNull(phone)){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_4000026);
        }
        LoginInfo loginInfo = loginInfoMapper.queryByPhone(phone);
        if (Objects.nonNull(loginInfo)){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_4000014);
        }
    }
}
